#include "stdafx.h"
#include <limits>
#include "problem_solver_core.h"

namespace nnse
{
	extern io_module io_m;

	// Constructor and Destructor

	template <class T> problem_solver_core<T>::problem_solver_core()
	{
		io_m.startup_stream << "Constructing problem_solver_core" << std::endl;
		number_of_training_examples=1;
		number_of_testing_examples=1;
		current_training_example=0;
		current_testing_example=0;
		number_of_inputs=1;
		number_of_targets=1;
		wrap_input=1;
		wrap_target=1;
		training_flag=true;
		input_flag=true;
		training_input_done=false;
		training_target_done=false;
		testing_input_done=false;
		testing_target_done=false;
	}

	template <class T> problem_solver_core<T>::~problem_solver_core()
	{
		io_m.startup_stream << "Destructing problem_solver_core" << std::endl;
	}

	// Accessor Functions

	template <class T> inline unsigned int problem_solver_core<T>::get_number_of_training_examples()
	{
		return number_of_training_examples;
	}

	template <class T> inline void problem_solver_core<T>::put_number_of_training_examples(unsigned int n_e)
	{
		number_of_training_examples=n_e;
	}

	template <class T> inline unsigned int problem_solver_core<T>::get_number_of_testing_examples()
	{
		return number_of_testing_examples;
	}

	template <class T> inline void problem_solver_core<T>::put_number_of_testing_examples(unsigned int n_e)
	{
		number_of_testing_examples=n_e;
	}

	template <class T> inline unsigned int problem_solver_core<T>::get_current_training_example()
	{
		return current_training_example;
	}

	template <class T> inline void problem_solver_core<T>::put_current_training_example(unsigned int c_e)
	{
		current_training_example=c_e;
	}

	template <class T> inline unsigned int problem_solver_core<T>::get_current_testing_example()
	{
		return current_testing_example;
	}

	template <class T> inline void problem_solver_core<T>::put_current_testing_example(unsigned int c_e)
	{
		current_testing_example=c_e;
	}

	template <class T> inline unsigned int problem_solver_core<T>::get_number_of_inputs()
	{
		return number_of_inputs;
	}

	template <class T> inline void problem_solver_core<T>::put_number_of_inputs(unsigned int n_i)
	{
		number_of_inputs=n_i;
	}

	template <class T> inline unsigned int problem_solver_core<T>::get_number_of_targets()
	{
		return number_of_targets;
	}

	template <class T> inline void problem_solver_core<T>::put_number_of_targets(unsigned int n_t)
	{
		number_of_targets=n_t;
	}

	template <class T> inline unsigned int problem_solver_core<T>::get_wrap_input()
	{
		return wrap_input;
	}

	template <class T> inline void problem_solver_core<T>::put_wrap_input(unsigned int w_i)
	{
		wrap_input=w_i;
	}

	template <class T> inline unsigned int problem_solver_core<T>::get_wrap_target()
	{
		return wrap_target;
	}

	template <class T> inline void problem_solver_core<T>::put_wrap_target(unsigned int w_t)
	{
		wrap_target=w_t;
	}

	template <class T> inline unsigned int problem_solver_core<T>::get_number_of_examples()
	{
		if (get_training_flag())
		{
			return get_number_of_training_examples();
		}
		else
		{
			return get_number_of_testing_examples();
		}
	}

	template <class T> inline void problem_solver_core<T>::put_number_of_examples(unsigned int n_e)
	{
		if (get_training_flag())
		{
			put_number_of_training_examples(n_e);
		}
		else
		{
			put_number_of_testing_examples(n_e);
		}
	}

	template <class T> inline unsigned int problem_solver_core<T>::get_current_example()
	{
		if (get_training_flag())
		{
			return get_current_training_example();
		}
		else
		{
			return get_current_testing_example();
		}
	}

	template <class T> inline void problem_solver_core<T>::put_current_example(unsigned int c_e)
	{
		if (get_training_flag())
		{
			put_current_training_example(c_e);
		}
		else
		{
			put_current_testing_example(c_e);
		}
	}

	template <class T> inline unsigned int problem_solver_core<T>::get_number_of_elements()
	{
		if (get_input_flag())
		{
			return get_number_of_inputs();
		}
		else
		{
			return get_number_of_targets();
		}
	}

	template <class T> inline unsigned int problem_solver_core<T>::get_wrap_element()
	{
		if (get_input_flag())
		{
			return get_wrap_input();
		}
		else
		{
			return get_wrap_target();
		}
	}

	template <class T> inline bool problem_solver_core<T>::get_training_flag()
	{
		return training_flag;
	}

	template <class T> inline void problem_solver_core<T>::put_training_flag()
	{
		training_flag=true;
	}

	template <class T> inline bool problem_solver_core<T>::get_testing_flag()
	{
		return !training_flag;
	}

	template <class T> inline void problem_solver_core<T>::put_testing_flag()
	{
		training_flag=false;
	}

	template <class T> inline bool problem_solver_core<T>::get_input_flag()
	{
		return input_flag;
	}

	template <class T> inline void problem_solver_core<T>::put_input_flag()
	{
		input_flag=true;
	}

	template <class T> inline bool problem_solver_core<T>::get_target_flag()
	{
		return !input_flag;
	}

	template <class T> inline void problem_solver_core<T>::put_target_flag()
	{
		input_flag=false;
	}

	template <class T> inline T problem_solver_core<T>::get_training_input_value(unsigned int i_n)
	{
		if (training_input_vector && i_n>=0 && i_n<training_input_vector->Length)
		{
   			return training_input_vector[i_n];
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::get_training_input_value");
		}
	}

	template <class T> inline void problem_solver_core<T>::put_training_input_value(unsigned int i_n, T v)
	{
   		if (training_input_vector && i_n>=0 && i_n<training_input_vector->Length)
		{
			training_input_vector[i_n]=v;
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::put_training_input_value");
		}
	}

	template <class T> inline T problem_solver_core<T>::get_training_target_value(unsigned int t_n)
	{
		if (training_target_vector && t_n>=0 && t_n<training_target_vector->Length)
		{
   			return training_target_vector[t_n];
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::get_training_target_value");
		}
	}

	template <class T> inline void problem_solver_core<T>::put_training_target_value(unsigned int t_n, T v)
	{
		if (training_target_vector && t_n>=0 && t_n<training_target_vector->Length)
		{
	   		training_target_vector[t_n]=v;
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::put_training_target_value");
		}
	}

	template <class T> inline T problem_solver_core<T>::get_testing_input_value(unsigned int i_n)
	{
		if (testing_input_vector && i_n>=0 && i_n<testing_input_vector->Length)
		{
			return testing_input_vector[i_n];
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::get_testing_input_value");
		}
	}

	template <class T> inline void problem_solver_core<T>::put_testing_input_value(unsigned int i_n, T v)
	{
		if (testing_input_vector && i_n>=0 && i_n<testing_input_vector->Length)
		{
			testing_input_vector[i_n]=v;
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::put_testing_input_value");
		}
	}

	template <class T> inline T problem_solver_core<T>::get_testing_target_value(unsigned int t_n)
	{
		if (testing_target_vector && t_n>=0 && t_n<testing_target_vector->Length)
		{
			return testing_target_vector[t_n];
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::get_testing_target_value");
		}
	}

	template <class T> inline void problem_solver_core<T>::put_testing_target_value(unsigned int t_n, T v)
	{
		if (testing_target_vector && t_n>=0 && t_n<testing_target_vector->Length)
		{
			testing_target_vector[t_n]=v;
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::put_testing_target_value");
		}
	}

	template <class T> inline T problem_solver_core<T>::get_value(unsigned int el)
	{
		if (get_training_flag() && get_input_flag())
		{
			return get_training_input_value((get_current_example()*get_number_of_inputs())+el);
		}
		if (get_training_flag() && get_target_flag())
		{
			return get_training_target_value((get_current_example()*get_number_of_targets())+el);
		}
		if (get_testing_flag() && get_input_flag())
		{
			return get_testing_input_value((get_current_example()*get_number_of_inputs())+el);
		}
		if (get_testing_flag() && get_target_flag())
		{
			return get_testing_target_value((get_current_example()*get_number_of_targets())+el);
		}
	}

	template <class T> inline T problem_solver_core<T>::get_input_value(unsigned int el)
	{
		if (get_training_flag())
		{
			return get_training_input_value((get_current_example()*get_number_of_inputs())+el);
		}
		if (get_testing_flag())
		{
			return get_testing_input_value((get_current_example()*get_number_of_inputs())+el);
		}
	}

	template <class T> inline T problem_solver_core<T>::get_target_value(unsigned int el)
	{
		if (get_training_flag())
		{
			return get_training_target_value((get_current_example()*get_number_of_targets())+el);
		}
		if (get_testing_flag())
		{
			return get_testing_target_value((get_current_example()*get_number_of_targets())+el);
		}
	}

	template <class T> inline void problem_solver_core<T>::put_value(unsigned int el, T v)
	{
		if (get_training_flag() && get_input_flag())
		{
			put_training_input_value((get_current_example()*get_number_of_inputs())+el, v);
		}
		if (get_training_flag() && get_target_flag())
		{
			put_training_target_value((get_current_example()*get_number_of_targets())+el, v);
		}
		if (get_testing_flag() && get_input_flag())
		{
			put_testing_input_value((get_current_example()*get_number_of_inputs())+el, v);
		}
		if (get_testing_flag() && get_target_flag())
		{
			put_testing_target_value((get_current_example()*get_number_of_targets())+el, v);
		}
	}

	template <class T> inline System::String^ problem_solver_core<T>::get_training_input_comment(unsigned int ex)
	{
		if (training_input_comments && ex>=0 && ex<training_input_comments->Length)
		{
			return gcnew System::String(training_input_comments[ex]);
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::get_training_input_comment");
		}
	}

	template <class T> inline void problem_solver_core<T>::put_training_input_comment(unsigned int ex, System::String^ comment)
	{
		if (training_input_comments && ex>=0 && ex<training_input_comments->Length)
		{
			training_input_comments[ex]=gcnew System::String(comment);
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::put_training_input_comment");
		}
	}

	template <class T> inline System::String^ problem_solver_core<T>::get_training_target_comment(unsigned int ex)
	{
		if (training_target_comments && ex>=0 && ex<training_target_comments->Length)
		{
			return gcnew System::String(training_target_comments[ex]);
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::get_training_target_comment");
		}
	}

	template <class T> inline void problem_solver_core<T>::put_training_target_comment(unsigned int ex, System::String^ comment)
	{
		if (training_target_comments && ex>=0 && ex<training_target_comments->Length)
		{
			training_target_comments[ex]=gcnew System::String(comment);
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::put_training_target_comment");
		}
	}

	template <class T> inline System::String^ problem_solver_core<T>::get_testing_input_comment(unsigned int ex)
	{
		if (testing_input_comments && ex>=0 && ex<testing_input_comments->Length)
		{
			return gcnew System::String(testing_input_comments[ex]);
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::get_testing_input_comment");
		}
	}

	template <class T> inline void problem_solver_core<T>::put_testing_input_comment(unsigned int ex, System::String^ comment)
	{
		if (testing_input_comments && ex>=0 && ex<testing_input_comments->Length)
		{
			testing_input_comments[ex]=gcnew System::String(comment);
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::put_testing_input_comment");
		}
	}

	template <class T> inline System::String^ problem_solver_core<T>::get_testing_target_comment(unsigned int ex)
	{
		if (testing_target_comments && ex>=0 && ex<testing_target_comments->Length)
		{
			return gcnew System::String(testing_target_comments[ex]);
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::get_testing_target_comment");
		}
	}

	template <class T> inline void problem_solver_core<T>::put_testing_target_comment(unsigned int ex, System::String^ comment)
	{
		if (testing_target_comments && ex>=0 && ex<testing_target_comments->Length)
		{
			testing_target_comments[ex]=gcnew System::String(comment);
		}
		else
		{
			throw std::out_of_range("\nError in problem_solver_core<T>::put_testing_target_comment");
		}
	}

	template <class T> inline System::String^ problem_solver_core<T>::get_comment()
	{
		if (get_training_flag() && get_input_flag())
		{
			return get_training_input_comment(get_current_example());
		}
		if (get_training_flag() && get_target_flag())
		{
			return get_training_target_comment(get_current_example());
		}
		if (get_testing_flag() && get_input_flag())
		{
			return get_testing_input_comment(get_current_example());
		}
		if (get_testing_flag() && get_target_flag())
		{
			return get_testing_target_comment(get_current_example());
		}
	}

	template <class T> inline System::String^ problem_solver_core<T>::get_input_comment()
	{
		if (get_training_flag())
		{
			return get_training_input_comment(get_current_example());
		}
		if (get_testing_flag())
		{
			return get_testing_input_comment(get_current_example());
		}
	}

	template <class T> inline System::String^ problem_solver_core<T>::get_target_comment()
	{
		if (get_training_flag())
		{
			return get_training_target_comment(get_current_example());
		}
		if (get_testing_flag())
		{
			return get_testing_target_comment(get_current_example());
		}
	}

	template <class T> inline void problem_solver_core<T>::put_comment(System::String^ comment)
	{
		if (get_training_flag() && get_input_flag())
		{
			put_training_input_comment(get_current_example(), comment);
		}
		if (get_training_flag() && get_target_flag())
		{
			put_training_target_comment(get_current_example(), comment);
		}
		if (get_testing_flag() && get_input_flag())
		{
			put_testing_input_comment(get_current_example(), comment);
		}
		if (get_testing_flag() && get_target_flag())
		{
			put_testing_target_comment(get_current_example(), comment);
		}
	}

	// Worker Functions

	template <class T> void problem_solver_core<T>::allocate_all_storage()
	{
		put_training_flag();
		put_input_flag();
		allocate_storage();
		put_training_flag();
		put_target_flag();
		allocate_storage();
		put_testing_flag();
		put_input_flag();
		allocate_storage();
		put_testing_flag();
		put_target_flag();
		allocate_storage();
	}

	template <class T> void problem_solver_core<T>::allocate_storage()
	{
		target_comments=gcnew array<System::String^>(get_number_of_targets());
		for (unsigned int t=0; t<get_number_of_targets(); t++)
		{
			target_comments[t]="";
		}
		if (get_training_flag() && get_input_flag())
		{
   			training_input_vector=gcnew array<T>(get_number_of_examples()*get_number_of_elements());
			training_input_comments=gcnew array<System::String^>(get_number_of_examples());
			for (unsigned int ex=0; ex<get_number_of_examples(); ex++)
			{
				put_training_input_comment(ex, "");
			}
			training_input_done=true;
		}
		if (get_training_flag() && get_target_flag())
		{
	   		training_target_vector=gcnew array<T>(get_number_of_examples()*get_number_of_elements());
			training_target_comments=gcnew array<System::String^>(get_number_of_examples());
			for (unsigned int ex=0; ex<get_number_of_examples(); ex++)
			{
				put_training_target_comment(ex, "");
			}
			training_target_done=true;
		}
		if (get_testing_flag() && get_input_flag())
		{
	   		testing_input_vector=gcnew array<T>(get_number_of_examples()*get_number_of_elements());
			testing_input_comments=gcnew array<System::String^>(get_number_of_examples());
			for (unsigned int ex=0; ex<get_number_of_examples(); ex++)
			{
				put_testing_input_comment(ex, "");
			}
			testing_input_done=true;
		}
		if (get_testing_flag() && get_target_flag())
		{
	   		testing_target_vector=gcnew array<T>(get_number_of_examples()*get_number_of_elements());
			testing_target_comments=gcnew array<System::String^>(get_number_of_examples());
			for (unsigned int ex=0; ex<get_number_of_examples(); ex++)
			{
				put_testing_target_comment(ex, "");
			}
			testing_target_done=true;
		}
	}

	template <class T> void problem_solver_core<T>::normalise_vector()
	{
		for (unsigned int ex=0; ex<get_number_of_examples(); ex++)
		{
			put_current_example(ex);
			T min_val;
			unsigned int min_el;
			get_min_val(min_val, min_el);
			T max_val;
			unsigned int max_el;
			get_max_val(max_val, max_el);
			T val_range=max_val - min_val;
			for(unsigned int el=0; el<get_number_of_elements(); el++)
			{
				T val=(get_value(el) - min_val) / val_range;
				put_value(el, val);
			}
		}
	}

	template <class T> void problem_solver_core<T>::get_min_val(T& min_val, unsigned int& min_el)
	{
		min_val=std::numeric_limits<T>::max();
		min_el=std::numeric_limits<unsigned int>::max();
		for(unsigned int el=0; el<get_number_of_elements(); el++)
		{
			T val=get_value(el);
			if (val < min_val)
			{
				min_val=val;
				min_el=el;
			}
		}
	}

	template <class T> void problem_solver_core<T>::get_max_val(T& max_val, unsigned int& max_el)
	{
		max_val=std::numeric_limits<T>::min();
		max_el=std::numeric_limits<unsigned int>::max();
		for(unsigned int el=0; el<get_number_of_elements(); el++)
		{
			T val=get_value(el);
			if (val > max_val)
			{
				max_val=val;
				max_el=el;
			}
		}
	}

	// Explicit Instantiation

	template ref class problem_solver_core<double>;
}
